package com.sunyzc.ebaotech.ybt.simulator;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sunyzc.ebaotech.ybt.simulator.util.CodeUtil;
import com.sunyzc.ebaotech.ybt.simulator.util.IOUtil;
import com.sunyzc.ebaotech.ybt.simulator.util.PropertyUtil;
import com.sunyzc.ebaotech.ybt.simulator.util.YBTUtil;

public abstract class AbstractProcessor implements IProcessor {
	private static final Logger log = LoggerFactory.getLogger(AbstractProcessor.class);
	private String serverHost = SERVER_HOST_YBT_LOCALHOST;
	private int dateOffSet = 0;
	private final static int BUFFER_SIZE = 16 * 1024;
	protected Map<String, String> transXmlMap = new HashMap<String, String>();

	@Override
	public String generateRequestBodyTemplateStr(String transType) throws Exception {
		String requestBodyContentStr = null;
		BufferedInputStream inputStream = null;
		ByteArrayOutputStream outputStream = null;
		try {
			inputStream = new BufferedInputStream(this.getClass().getResourceAsStream(transType + ".xml"));
			outputStream = new ByteArrayOutputStream();
			byte[] buffer = new byte[BUFFER_SIZE];
			int readLength = 0;
			while ((readLength = inputStream.read(buffer)) != -1)
				outputStream.write(buffer, 0, readLength);
			requestBodyContentStr = new String(outputStream.toByteArray());
		} finally {
			IOUtil.closeQuietly(inputStream);
			IOUtil.closeQuietly(outputStream);
		}
		return requestBodyContentStr;
	}

	@Override
	public byte[] generateRequestBodyBytes(String transType, String requestBodyTemplateStr) throws Exception {
		String requestBodyContentStr = (requestBodyTemplateStr != null && !requestBodyTemplateStr.trim().equals("")) ? requestBodyTemplateStr : generateRequestBodyTemplateStr(transType);
		requestBodyContentStr = requestBodyContentStr.replaceAll("<!-- （.*）.* -->", ""); // 去除报文中符合规则的注释，但不包含标准注释
		Calendar currentDateCalendar = new GregorianCalendar();
		currentDateCalendar.add(Calendar.DATE, dateOffSet);
		String currentDateTimeStr = new SimpleDateFormat(getDateFormatStr(transType)[0]).format(currentDateCalendar.getTime());
		String currentDateStr = new SimpleDateFormat(getDateFormatStr(transType)[1]).format(currentDateCalendar.getTime());
		String currentTimeStr = new SimpleDateFormat(getDateFormatStr(transType)[2]).format(currentDateCalendar.getTime());
		if (requestBodyContentStr.contains("@TRANS_CODE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TRANS_CODE@", "Sun" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new GregorianCalendar().getTime())); // 交易流水号，根据实际本地当前时间生成
		if (requestBodyContentStr.contains("@TRANS_DATE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TRANS_DATE@", currentDateStr); // 交易日期，服务器今日日期
		if (requestBodyContentStr.contains("@APPLY_DATE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@APPLY_DATE@", currentDateStr); // 投保日期，服务器今日日期
		if (requestBodyContentStr.contains("@TRANS_TIME@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TRANS_TIME@", currentTimeStr); // 交易时间，本地当前时间
		if (requestBodyContentStr.contains("@TRANS_DATE_TIME@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TRANS_DATE_TIME@", currentDateTimeStr); // 交易日期和时间，服务器今日日期和本地当前时间
		if (requestBodyContentStr.contains("@EFFECT_DATE@")) {
			Calendar effectDateCalendar = new GregorianCalendar();
			effectDateCalendar.setTime(currentDateCalendar.getTime());
			effectDateCalendar.add(Calendar.DATE, 1); // 设置生效日期为服务器今日日期的次日
			String effectDateStr = new SimpleDateFormat(getDateFormatStr(transType)[1]).format(effectDateCalendar.getTime());
			requestBodyContentStr = requestBodyContentStr.replace("@EFFECT_DATE@", effectDateStr);
		}
		if (requestBodyContentStr.contains("@APPLY_CODE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@APPLY_CODE@", CodeUtil.generateCommonCode(PropertyUtil.getNexSerialNum(), "005")); // 投保单号
		if (requestBodyContentStr.contains("@POLICY_CODE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@POLICY_CODE@", CodeUtil.generateCommonCode(PropertyUtil.getNexSerialNum(), "088")); // 保单号
		if (requestBodyContentStr.contains("@PRINT_CODE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@PRINT_CODE@", PropertyUtil.getNextPrintCode()); // 印刷号

		Matcher matcher = Pattern.compile("@((RQ|RP).+?-\\w+)@").matcher(requestBodyContentStr); // 原先的\\w+替换为.+?，因为\w匹配字母或数字或下划线或汉字，而.匹配除换行符以外的任意字符，另外+?为懒惰匹配(重复1次或更多次但尽可能少重复)
		// 正则查找和替换保单号、保费等字段
		while (matcher.find()) {
			String matcherGroup = matcher.group(1);
			if (requestBodyContentStr.contains("@" + matcherGroup + "@")) {
				String elementValue = getElementValue(matcherGroup);
				if (YBTUtil.isNeedDiv100(this, transType, matcherGroup))
					elementValue = elementValue.contains(".") ? String.format("%.2f", Double.parseDouble(elementValue) / 100) : Long.parseLong(elementValue) / 100 + "";
				requestBodyContentStr = requestBodyContentStr.replace("@" + matcherGroup + "@", elementValue);
			}
		}

		// ------------------------------------------------------以下处理日结对账报文-------------------------------------------------------
		int dayCheckSequence = 0;
		// 统计对账Xml的@TOTAL_COUNT@并替换对账保单序号@DAYCHECK_SEQUENCE@，注意一定要是懒惰匹配
		Matcher dayCheckXmlDetailMatcher = Pattern.compile("(<DAYCHECK_DETAIL>[\\w\\W]*?</DAYCHECK_DETAIL>)").matcher(requestBodyContentStr);
		while (dayCheckXmlDetailMatcher.find()) {
			++dayCheckSequence;
			if (requestBodyContentStr.contains("@DAYCHECK_SEQUENCE@")) { // 如果存在序号则逐一替换序号
				String dayCheckXmlDetailContentStr = dayCheckXmlDetailMatcher.group(1); // 获取日结对账Xml内容中单个对账信息节点内容
				requestBodyContentStr = requestBodyContentStr.replaceFirst(dayCheckXmlDetailContentStr, "@DAYCHECK_XML_DETAIL_CONTENT@"); // 获取并替换掉第一个(注意是第一个)dayCheckXmlDetailContentStr
				dayCheckXmlDetailContentStr = dayCheckXmlDetailContentStr.replace("@DAYCHECK_SEQUENCE@", dayCheckSequence + "");
				requestBodyContentStr = requestBodyContentStr.replace("@DAYCHECK_XML_DETAIL_CONTENT@", dayCheckXmlDetailContentStr);
			}
		}
		requestBodyContentStr = requestBodyContentStr.replace("@TOTAL_COUNT@", dayCheckSequence + "");
		// ------------------------------------------------------日结对账报文处理结束-------------------------------------------------------

		log.info("交易[" + transType + "]请求报文体: " + requestBodyContentStr);
		transXmlMap.put("RQ" + transType, requestBodyContentStr);
		byte[] requestBodyContentBytes = beforeSend(requestBodyContentStr);
		return requestBodyContentBytes;
	}

	@Override
	public byte[] generateResponseBodyBytes(String transType) throws Exception {
		return beforeSend("这是 [" + transType + "] 交易由服务器端返回的结果内容。");
	}

	@Override
	public byte[] convertContentStrToBytes(String contentStr) throws Exception {
		return contentStr.getBytes(getTransEncoding());
	}

	@Override
	public String convertContentBytesToStr(byte[] contentBytes) throws Exception {
		return new String(contentBytes, getTransEncoding());
	}

	@Override
	public String readHeadConetentStr(String transType, InputStream inputStream) throws IOException {
		String headContentStr = null;
		int readLength = 0;
		byte[] byteHeader = new byte[getHeadLength()];
		while (readLength != byteHeader.length)
			readLength += inputStream.read(byteHeader, readLength, byteHeader.length - readLength);
		headContentStr = new String(byteHeader, getTransEncoding());
		return headContentStr;
	}

	@Override
	public String readBodyConetentStr(String transType, InputStream inputStream, String headContentStr) throws Exception {
		String bodyContentStr = null;
		int readLength = 0;
		int bodyLength = Integer.parseInt(headContentStr.substring(getBodyLengthBeginIndex(), getBodyLengthEndIndex()).trim());
		byte[] byteBody = new byte[bodyLength];
		while (readLength != bodyLength)
			readLength += inputStream.read(byteBody, readLength, bodyLength - readLength);
		log.debug("交易[" + transType + "]响应未处理的报文体: " + convertContentBytesToStr(byteBody));
		bodyContentStr = convertContentBytesToStr(afterReceived(byteBody));
		transXmlMap.put("RP" + transType, bodyContentStr);
		return bodyContentStr;
	}

	@Override
	public String getServerHost() {
		return serverHost;
	}

	@Override
	public void setServerHost(String serverHost) {
		this.serverHost = serverHost;
	}

	@Override
	public void setDateOffSet(int dateOffset) {
		this.dateOffSet = dateOffset;
	}

	@Override
	public void setCurrentDate(String currentDateStr) {
		try {
			if (currentDateStr != null && !currentDateStr.trim().equals("")) {
				DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
				Calendar settingCalendar = Calendar.getInstance();
				Calendar currentCalendar = Calendar.getInstance();
				settingCalendar.setTime(dateFormat.parse(currentDateStr));
				currentCalendar.setTime(dateFormat.parse(dateFormat.format(currentCalendar.getTime())));
				dateOffSet = (int) ((settingCalendar.getTimeInMillis() - currentCalendar.getTimeInMillis()) / (1000 * 60 * 60 * 24));
			}
		} catch (ParseException e) {
			log.error(e.getMessage(), e);
		}
	}

	@Override
	public String getElementValue(String transXmlMapKey) {
		return getElementValue(transXmlMapKey, "");
	}

	@Override
	public String getElementValue(String transXmlMapKey, String defaultValue) {
		return getElementValue(transXmlMap.get(transXmlMapKey.split("-")[0]), transXmlMapKey.split("-")[1], defaultValue);
	}

	private String getElementValue(String xmlString, String elementName, String defaultValue) {
		String elementValue = defaultValue;
		if (xmlString != null && !"".equals(xmlString.trim())) {
			Matcher matcher = Pattern.compile("<" + elementName + ">" + "([^<]*)" + "</" + elementName + ">").matcher(xmlString);
			if (matcher.find())
				elementValue = matcher.group(1);
		}
		return elementValue;
	}

	/** 对报文体做加密、压缩等处理 */
	protected byte[] beforeSend(String bodyContentStr) throws Exception {
		return bodyContentStr.getBytes(getTransEncoding());
	}

	/** 对报文体做解压缩、解密等处理 */
	protected byte[] afterReceived(byte[] bodyContentBytes) throws Exception {
		return bodyContentBytes;
	}

	/** 返回长度为3的字符串数组，第0个为日期时间，第1一个为日期，第2个为时间 */
	protected String[] getDateFormatStr(String transType) {
		return new String[] { "yyyyMMddHHmmss", "yyyyMMdd", "HHmmss" };
	}

	protected abstract String getTransEncoding();

	protected abstract int getHeadLength();

	protected abstract int getBodyLengthBeginIndex();

	protected abstract int getBodyLengthEndIndex();
}
